#include "Ascensor.h"
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include "../lib/Constantes.h"
#include "../lib/Util.h"
#include "../lib/Logger.h"

#define PROCESS_NAME "ASCENSOR"

using namespace std;

void Ascensor::log(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": (" + Util::intToString(this->numero) + ") "
			+ message;
	logger->loggearLog(msg);
}

void Ascensor::debug(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": (" + Util::intToString(this->numero) + ") "
			+ message;
	logger->loggearDebug(msg);
}

void Ascensor::sync(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": (" + Util::intToString(this->numero) + ") "
			+ message;
	logger->loggearSynchro(msg);
}

Ascensor::Ascensor(int cantPisos, int velocidad, bool modoDebug, bool modoLog,
		bool modoSyn, bool modoCout, int cantidadAscensores, int capacidad,
		int numero_asc) {
	//Seteamos los flags del logger
	Logger::getInstance()->setFlags(modoDebug, modoLog, modoSyn, modoCout);
	this->logger = Logger::getInstance();

	this->velocidad = velocidad;
	this->estado = SUBIENDO;
	this->numero = numero_asc;
	this->mAscensor = new MonitorAscensores(cantidadAscensores, cantPisos,
			capacidad, velocidad);
	this->mPisos = new MonitorPisos(cantPisos);
	this->manejadorSemAsc = new ManejadorSemAscensores(cantidadAscensores);
	this->manejadorSemPisos = new ManejadorSemPisos(cantPisos);
	this->debug("Manejadores y Monitores creados correctamente.");
	this->cola = new Cola<mensaje> ((char*) CONTROLADOR_ARCHIVO,
			(char) CONTROLADOR_LETRA);
	this->debug("Cola de envío de peticiones creada correctamente.");

	this->log("Ascensor listo para funcionar.");
}

Ascensor::~Ascensor() {
	delete (this->mAscensor);
	delete (this->mPisos);
	delete (this->manejadorSemAsc);
	delete (this->manejadorSemPisos);
	delete (this->cola);
}

void Ascensor::abrirParaEgreso(int piso) {
	this->log("Llegamos al piso " + Util::intToString(piso)
			+ " Habilitamos Egreso.");

	tascensor ascensor = this->mAscensor->leer(this->numero);
	this->log("Cantidad de pasajeros para avisar que salgan "
			+ Util::intToString(ascensor.cantPersonas));

	if (ascensor.cantPersonas > 0) {
		this->debug("Se abren las puertas para salir del ascensor");
		this->manejadorSemAsc->abrir(this->numero, ascensor.cantPersonas);
		this->manejadorSemAsc->cerrar(this->numero, ascensor.cantPersonas);
		this->debug("Se cierran las puertas para salir del ascensor");
	}
}
void Ascensor::habilitarIngresoSubida(int piso) {

	/*Bloqueo de las personas en el piso*/
	int cantPis = this->mPisos->bloquearPiso(piso, SUBIENDO);

	this->log("Cantidad de personas para subir " + Util::intToString(cantPis));
	this->log("Llegamos al piso " + Util::intToString(piso)
			+ " Habilitamos Ingreso.");

	tascensor ascensor = this->mAscensor->leer(this->numero);
	int cantPasajerosAntes = ascensor.cantPersonas;

	if (cantPis > 0) {
		this->debug(
				"Abro las puertas para entrar al ascensor en direccion SUBIENDO");
		this->mAscensor->abrir(this->numero);
		this->manejadorSemPisos->abrir(piso, SUBIENDO, cantPis);
		this->debug(
				"Cierro las puertas para entrar al ascensor en direccion SUBIENDO");
		this->mAscensor->cerrar(this->numero);
		this->debug("Listo para arrancar desde el piso " + Util::intToString(
				piso) + " SUBIENDO");
		this->manejadorSemPisos->cerrar(piso, SUBIENDO, cantPis);

		ascensor = this->mAscensor->leer(this->numero);
	}

	/*Desbloqueo de las personas en el piso*/
	this->mPisos->actualizar(piso, SUBIENDO, ascensor.cantPersonas
			- cantPasajerosAntes);

	this->log("En el piso " + Util::intToString(piso)
			+ " entraron al ascensor " + Util::intToString(
			ascensor.cantPersonas - cantPasajerosAntes)
			+ " personas en direccion SUBIENDO.");

}

void Ascensor::habilitarIngresoBajada(int piso) {

	/*Bloqueo de las personas en el piso*/
	int cantPis = this->mPisos->bloquearPiso(piso, BAJANDO);

	this->log("Cantidad de personas para bajar " + Util::intToString(cantPis));
	this->log("Llegamos al piso " + Util::intToString(piso)
			+ " Habilitamos Ingreso.");

	tascensor ascensor = this->mAscensor->leer(this->numero);
	int cantPasajerosAntes = ascensor.cantPersonas;

	if (cantPis > 0) {

		this->debug(
				"Abro las puertas para entrar al ascensor en direccion BAJANDO");
		this->mAscensor->abrir(this->numero);
		this->manejadorSemPisos->abrir(piso, BAJANDO, cantPis);
		this->debug(
				"Cierro las puertas para entrar al ascensor en direccion BAJANDO");
		this->mAscensor->cerrar(this->numero);
		this->debug("Listo para arrancar desde el piso " + Util::intToString(
				piso) + " BAJANDO");
		this->manejadorSemPisos->cerrar(piso, BAJANDO, cantPis);

		ascensor = this->mAscensor->leer(this->numero);
	}

	/*Desbloqueo de las personas en el piso*/
	this->mPisos->actualizar(piso, BAJANDO, ascensor.cantPersonas
			- cantPasajerosAntes);

	this->log("En el piso " + Util::intToString(piso)
			+ " entraron al ascensor " + Util::intToString(
			ascensor.cantPersonas - cantPasajerosAntes)
			+ " personas en direccion BAJANDO.");

}

void Ascensor::irProxPiso() {

	this->manejadorSemAsc->esperarProxPisos(this->numero);
	tascensor ascensor = this->mAscensor->leer(this->numero);
	this->proxPiso = ascensor.proxPiso;

	this->log("El controlador me envio al piso " + Util::intToString(
			this->proxPiso));

	if (this->proxPiso == -1) {
		return;
	}

	this->estado = ascensor.estado;
	char sentido[40];
	sprintf(sentido, "Estamos en el sentido %c", this->estado);
	this->debug(sentido);

	while (this->proxPiso != ascensor.pisoActual) {
		if (this->estado == SUBIENDO) {
			this->debug("Estaba en el piso " + Util::intToString(
					ascensor.pisoActual) + " , aumento uno.");
			ascensor.pisoActual++;
		} else if (this->estado == BAJANDO) {
			this->debug("Estaba en el piso " + Util::intToString(
					ascensor.pisoActual) + " , disminuyo uno.");
			ascensor.pisoActual--;
		}
		sleep(ALTURA / velocidad);
	}
	this->mAscensor->arriboPiso(this->numero, ascensor.pisoActual);

}
void Ascensor::mandarMensaje() {
	mensaje peticion;
	peticion.nroAscensor = this->numero;
	peticion.pid = getpid();
	this->cola->escribir(peticion);
	this->debug("Peticion de piso enviada al CC");
}

int Ascensor::start() {
	while (1 == 1) {
		this->mandarMensaje();
		this->irProxPiso();

		/* Si leemos un piso negativo quiere decir que me tengo que morir.*/
		if (this->proxPiso < 0) {
			this->log("El CC me dio muerte. Muero.");
			return -1;
		}

		this->abrirParaEgreso(this->proxPiso);

		if (this->estado == SUBIENDO) {
			this->habilitarIngresoSubida(this->proxPiso);
		} else {
			this->habilitarIngresoBajada(this->proxPiso);
		}
	}
	return 0;
}
